<?php


namespace coco;


use coco\model\Relation;

/**
 * Class Model
 * @package coco
 * @property-read string $table
 * @property-read string $alias
 */
abstract class Model
{
    private static $new_map = [];
    protected $table;
    protected $alias;
    protected $options;
    private $relations = [];
    private $is_has_many = false;
    protected $validate = [];
    protected $auto = [];

    public function __get($name)
    {
        if (in_array($name, ['table', 'alias'])) {
            return $this->$name;
        }
        return null;
    }

    public function __construct()
    {
        if (empty($this->table)) {
            $this->table = class2url(class_basename(static::class));
        }
        self::$new_map[static::class] = (self::$new_map[static::class] ?? 0) + 1;
        $this->alias = $this->table . '_' . (self::$new_map[static::class]);
    }

    public function __destruct()
    {
        self::$new_map[static::class]--;
        if (empty(self::$new_map[static::class])) {
            unset(self::$new_map[static::class]);
        }
    }

    /**
     * @param string $with
     * @return $this
     *
     * @example
     * <pre>
     * $news->with('user.profile');
     * hasMany的聚合查询
     * $news->with('user.news:id|count');
     * </pre>
     */
    public function with(string $with): self
    {
        if (empty($this->options['with'])) {
            $this->options['with'] = [];
        }
        if (!empty($with)) {
            if (!is_array($with)) {
                $with = explode(',', $with);
            }
            foreach ($with as $item) {
                $this->options['with'][] = $item;
            }
        }
        return $this;
    }

    /**
     * @param string $with
     * @param array $where
     * @return $this
     *
     * @example
     * <pre>
     * $user->hasWhere('user',['id'=>1]);
     * $user->hasWhere('user.profile',['age'=>['lt',10]]);
     * </pre>
     */
    public function hasWhere(string $with, array $where): self
    {
        if (empty($this->options['hasWhere'])) {
            $this->options['hasWhere'] = [];
        }
        $this->options['hasWhere'][$with] = array_merge($this->options['hasWhere'][$with] ?? [], $where);
        return $this;
    }

    /**
     * @param string $with
     * @param string $field
     * @param string $fn
     * @param $value
     * @return $this
     *
     * @example
     * <pre>
     * $user->having('user','id','count',1);
     * $user->having('with','field','func',value);
     * $user->having('with','id','avg',['>',1]);
     * $user->having('with','field','func',['op',value]);
     * </pre>
     */
    public function having(string $with, string $field, string $fn, $value): self
    {
        $fn = strtoupper($fn);
        if (!in_array($fn, ['COUNT', 'AVG', 'SUM', 'MAX', 'MIN'])) {
            return $this;//无效设置忽略
        }
        if (empty($this->options['having'])) {
            $this->options['having'] = [];
        }
        $this->options['having'][$with] = array_merge($this->options['having'][$with] ?? [], [$field, $fn, $value]);
        return $this;
    }

    public function where(array $where): self
    {
        if (empty($this->options['where'])) {
            $this->options['where'] = [];
        }
        $this->options['where'][] = $where;
        return $this;
    }

    public function buildSql(bool $build = true)
    {
        $this->options['build_sql'] = $build;
        return $this;
    }

    /**
     * @param array $order ['id'=>'DESC'],['id']===['id'=>'ASC']
     * @return $this
     */
    public function order(array $order): self
    {
        $this->options['order'] = array_merge($this->options['order'] ?? [], $order);
        return $this;
    }

    public function limit(int $offset, int $size = 20): self
    {

        if (empty($size)) {
            $size = $offset;
            $offset = 0;
        }
        $this->options['limit'] = compact('offset', 'size');
        return $this;
    }

    public function page(int $page, int $size = 10): self
    {
        $offset = ($page - 1) * $size;
        $this->options['limit'] = compact('offset', 'size');
        return $this;
    }

    /**
     * 注入options的db对象
     */
    public function parseDb()
    {
        if (false === $options = $this->parseOptions()) {
            return false;
        }
        return Db::getInstance()->setOptions($options);
    }


    public function select()
    {
        if (false === $options = $this->parseOptions()) {
            return false;
        }
        //query
        $resultSet = Db::getInstance()->select($options);
        if (!empty($options['build_sql'])) {
            return $resultSet;
        }
        if (!empty($resultSet)) {
            foreach ($resultSet as &$result) {
                //重新组合结果
                self::completeData($result, $options['__result_alias_with']);
                self::call_after($this, $result, $options['__result_with']);
            }
        }
        return $resultSet;
    }


    public function find()
    {
        if (false === $options = $this->parseOptions()) {
            return false;
        }
        //query
        $result = Db::getInstance()->find($options);
        if (!empty($options['build_sql'])) {
            return $result;
        }
        //重新组合结果
        self::completeData($result, $options['__result_alias_with']);
        if (!empty($result)) {
            self::call_after($this, $result, $options['__result_with']);
        }
        return $result;
    }


    /**
     * 验证+自动生成数据
     * @param array $data
     * @param int $scene
     * @return array|false
     */
    public function create(array $data, int $scene)
    {
        if (false === $db_fields = $this->getDbFields()) {
            return false;
        }
        $fields = array_column($db_fields, 'field');
        //验证数据
        if (false === $this->validate($data, $scene)) {
            return false;
        }
        //自动生成
        if (false === $data = $this->auto($data, $scene)) {
            return false;
        }
        //删除多余数据
        foreach ($data as $k => $v) {
            if (!in_array($k, $fields)) {
                unset($data[$k]);
            }
        }

        return $data;
    }

    private function validate(array $data, int $scene)
    {
        $validate = $this->validate ?? [];
        if (!empty($validate)) {
            foreach ($validate as $k => $v) {
                $field_name = '字段`' . $k . '`';
                if (false !== $pos = strpos($k, '|')) {
                    $field_name = substr($k, $pos + 1);
                    $k = substr($k, 0, $pos);
                }
                $cur_scene = 3;//默认新增修改
                if (false !== $pos = strpos($k, ':')) {
                    $cur_scene = (int)substr($k, $pos + 1);
                    $k = substr($k, 0, $pos);
                }
                //存在才验证
                if (!array_key_exists($k, $data)) {
                    continue;
                }
                if (($scene & $cur_scene) === $scene) {
                    $validate_item = explode('|', $v);
                    foreach ($validate_item as $item) {
                        if (false === strpos($item, ':')) {
                            $rule = $item;
                            $param = '';
                        } else {
                            list($rule, $param) = explode(':', $item, 2);
                        }
                        if (false === $rule_caller = $this->getRule($rule, $k)) {
                            $this->error = '无效的规则:' . $rule;
                            return false;
                        }
                        if (in_array($rule, ['cb', 'fn'])) {
                            $params = [];
                            $func = $param;
                            if (false !== $lpos = strpos($param, '(')) {
                                $func = substr($param, 0, $lpos);
                                $params = explode(',', substr($param, $lpos + 1, -1));
                            }
                            foreach ($params as &$p) {
                                if ($p === '__data') {
                                    $p = $data;
                                } elseif (array_key_exists($p, $data)) {
                                    $p = $data[$p];
                                }
                            }
                            unset($p);
                            $params = [$func, $params];
                        } else {
                            $params = $param !== '' ? explode(',', $param, 2) : [];
                        }
                        array_unshift($params, $data[$k] ?? '');
                        $called = call_user_func($rule_caller['fn'], ...$params);
                        if (false === $called) {
                            $this->error = str_replace([':attribute', ':arg1', ':arg2'], [$field_name, $params[1] ?? '', $params[2] ?? ''], $rule_caller['msg']);
                            return false;
                        }
                        if (is_array($called)) {
                            list($err, $msg) = $called;
                            if (false === $err) {
                                $this->error = $msg;
                                return false;
                            }
                        }
                    }
                }
            }
        }
        return true;
    }

    private function getRule(string $rule, string $field)
    {
        $op_map = [
            'min' => '>=',
            'egt' => '>=',
            'gt' => '>',
            'lt' => '<',
            'max' => '<=',
            'elt' => '<=',
        ];
        $rule = $op_map[$rule] ?? $rule;
        $rules = [
            'required' => [
                'msg' => ':attribute不能为空',
                'fn' => function ($value) {
                    if ($value === '' || $value === null) {
                        return false;
                    } else {
                        return true;
                    }
                }
            ],
            'numeric' => [
                'msg' => ':attribute必须是数字',
                'fn' => function ($value) {
                    return is_numeric($value);
                }
            ],
            'int' => [
                'msg' => ':attribute必须是整数',
                'fn' => function ($value) {
                    return filter_var($value, FILTER_VALIDATE_INT) !== false;
                }
            ],
            '>=' => [
                'msg' => ':attribute不能小于:arg1',
                'fn' => function ($value, $arg1) {
                    return $value >= $arg1;
                }
            ],
            '<=' => [
                'msg' => ':attribute不能大于:arg1',
                'fn' => function ($value, $arg1) {
                    return $value <= $arg1;
                }
            ],
            '>' => [
                'msg' => ':attribute必须大于:arg1',
                'fn' => function ($value, $arg1) {
                    return $value > $arg1;
                }
            ],
            '<' => [
                'msg' => ':attribute必须小于:arg1',
                'fn' => function ($value, $arg1) {
                    return $value < $arg1;
                }
            ],
            'between' => [
                'msg' => ':attribute必须介于:arg1-:arg2之间',
                'fn' => function ($value, $arg1, $arg2) {
                    return $value >= $arg1 && $value <= $arg2;
                }
            ],
            'min_len' => [
                'msg' => ':attribute不能短于:arg1',
                'fn' => function ($value, $arg1) {
                    return strlen($value) >= $arg1;
                }
            ],
            'max_len' => [
                'msg' => ':attribute不能长于:arg1',
                'fn' => function ($value, $arg1) {
                    return strlen($value) <= $arg1;
                }
            ],
            'between_len' => [
                'msg' => ':attribute长度必须介于:arg1-:arg2个字符之间',
                'fn' => function ($value, $arg1, $arg2) {
                    return strlen($value) >= $arg1 && strlen($value) <= $arg2;
                }
            ],
            'uint' => [
                'msg' => ':attribute必须为大于0的正整数',
                'fn' => function ($value) {
                    $v = filter_var($value, FILTER_VALIDATE_INT);
                    return $v && $v > 0;
                }
            ],
            'email' => [
                'msg' => ':attribute格式不正确',
                'fn' => function ($value) {
                    return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
                }
            ],
            'ip' => [
                'msg' => ':attribute格式不正确',
                'fn' => function ($value) {
                    return filter_var($value, FILTER_VALIDATE_IP) !== false;
                }
            ],
            'unique' => [
                'msg' => ':attribute已存在',
                'fn' => function ($value) use ($field) {
                    if (false === $has = $this->singleton()->field($field)->where([$field => $value])->find()) {
                        return [false, 'SYSTEM ERROR'];
                    }
                    return empty($has);
                }
            ],
            'fn' => [
                'msg' => ':attribute未通过`:arg1`方法验证',
                'fn' => function ($value, $func = '', $params = []) {
                    if ($func && !function_exists($func)) {
                        return false;
                    }
                    array_unshift($params, $value);
                    return call_user_func($func, ...$params);
                }
            ],
            'cb' => [
                'msg' => ':attribute未通过`' . static::class . ':::arg1`方法验证',
                'fn' => function ($value, $func = '', $params = []) {
                    if ($func && !method_exists(static::class, $func)) {
                        return false;
                    }
                    array_unshift($params, $value);
                    return call_user_func([static::class, $func], ...$params);
                }
            ]
        ];
        return $rules[$rule] ?? false;
    }

    private function auto(array $data, int $scene)
    {
        $auto = $this->auto ?? [];
        if (!empty($auto)) {
            foreach ($auto as $k => $v) {
                $cur_scene = 3;//默认新增修改
                if (false !== $pos = strpos($k, ':')) {
                    $cur_scene = (int)substr($k, $pos + 1);
                    $k = substr($k, 0, $pos);
                }
                if (($scene & $cur_scene) === $scene) {
                    $items = explode('|', $v);
                    foreach ($items as $item) {
                        if ('ignore' === $item && empty($data[$k])) {
                            unset($data[$k]);
                            break;
                        } elseif ('time' === $item) {
                            $data[$k] = time();
                            break;
                        } elseif (false !== strpos($item, ':')) {
                            list($type, $func) = explode(':', $item, 2);
                            


                            if (in_array($type, ['cb', 'fn'])) {
                                $params = [];
                                $param = $func;
                                if (false !== $lpos = strpos($param, '(')) {
                                    $func = substr($param, 0, $lpos);
                                    $params = explode(',', substr($param, $lpos + 1, -1));
                                }
                                foreach ($params as &$p) {
                                    if ($p === '__data') {
                                        $p = $data;
                                    } elseif (array_key_exists($p, $data)) {
                                        $p = $data[$p];
                                    }
                                }
                                unset($p);
                            } else {
                                $params = [];
                            }
                            if(isset($data[$k]))
                            {
                                array_unshift($params, $data[$k] ?? '');
                            }
                            if ('fn' === $type) {
                                if (function_exists($func)) {
                                    $data[$k] = call_user_func($func, ...$params);
                                }
                            } elseif ('cb' === $type) {
                                if (method_exists(static::class, $func)) {
                                    $data[$k] = call_user_func([static::class, $func], ...$params);
                                }

                            } else {
                                //其他带扩展
                            }
                        } else {
                            //默认值
                            if (empty($data[$k])) {
                                $data[$k] = $item;
                            }
                        }
                    }
                }
            }
        }
        return $data;
    }


    public function add(array $data, bool $with_out_create = false)
    {
        if (false === $with_out_create) {
            if (false === $data = $this->create($data, 1)) {
                return false;
            }
        }
        if (false === $ret = Db::getInstance()->table($this->table)->insert($data)) {
            $this->error = Db::getInstance()->getError();
            return false;
        }

        return $ret;
    }

    public function addAll(array $data_lists, bool $with_out_create = false)
    {
        if (false === $with_out_create) {
            $datas = [];
            foreach ($data_lists as $data) {
                if (false === $datas[] = $this->create($data, 1)) {
                    return false;
                }
            }
        } else {
            $datas = $data_lists;
        }
        if (false === $ret = Db::getInstance()->table($this->table)->insertAll($datas)) {
            $this->error = Db::getInstance()->getError();
            return false;
        }
        return $ret;
    }

    //todo 删
    public function delete()
    {
        if (false === $options = $this->parseOptions()) {
            return false;
        }
        if (false === $ret = Db::getInstance()->delete($options)) {
            $this->error = Db::getInstance()->getError();
            return false;
        }
        if (!empty($options['build_sql'])) {
            return $ret;
        }
        return $ret;
    }

    public function save(array $data, bool $with_out_create = false)
    {
        if (false === $options = $this->parseOptions()) {
            return false;
        }
        if (empty($options['where']) && !empty($data['id'])) {
            $options['where'][] = [
                'id' => $data['id']
            ];
        }
        if (empty($options['where'])) {
            $this->error = '更新条件为空';
            return false;
        }
        if (false === $with_out_create) {
            if (false === $data = $this->create($data, 2)) {
                return false;
            }
        }
        if (false === $ret = Db::getInstance()->update($data, $options)) {
            $this->error = Db::getInstance()->getError();
            return false;
        }
        if (!empty($options['build_sql'])) {
            return $ret;
        }
        return $ret;
    }


    private static function call_after(self $that, array &$result, array $with)
    {
        if (empty($with)) {
            return;
        }
        $sub_with = self::parseWith($with);
        $withs = [];
        foreach ($sub_with as $w => $sub) {
            $withs[] = $w;
            if (!empty($result[$w])) {
                /**@var Relation $relation */
                /**@var Model $that */
                if ($relation = $that->getRelation($w)) {
                    self::call_after($relation->getModel(), $result[$w], $sub);
                }
            }
        }
        if (!empty($result)) {
            $that->after($result, $withs, $sub_with);//递归调用
        }
    }

    public function singleton()
    {
        return Db::getInstance()->table($this->table);
    }

    /**
     * @return array|false
     */
    private function parseOptions()
    {
        $db_options = [];
        $withs = array_merge(
            $this->options['with'] ?? [],
            !empty($this->options['hasWhere']) ? array_keys($this->options['hasWhere']) : [],
            !empty($this->options['having']) ? array_keys($this->options['having']) : []
        );
        $db_options['join'] = self::getJoinOptions($this, self::parseWith($withs));
        $options = $this->options;
        $this->options = [];//清空对象
        if (!empty($options['build_sql'])) {
            $db_options['build_sql'] = true;
        }
        //set table
        $db_options['table'] = $this->table;
        $db_options['alias'] = $this->alias;
        if (!empty($options['group'])) {//如果有hasmany
            $db_options['group'] = $this->alias . '.`id`';
            //set having
            if (!empty($options['having'])) {
                $db_having = [];
                foreach ($options['having'] as $with => $having) {
                    if ($that = self::pathFindModel($this, $with)) {
                        list($field, $fn, $value) = $having;
                        if (!is_array($value)) {
                            $value = ['=', $value];
                        }
                        list($op, $val) = $value;
                        $db_having[] = $fn . '(' . $that->alias . '.`' . $field . '`)' . $op . $val;
                        $db_options['having'] = join(' AND ', $db_having);
                        unset($fn);
                    }
                }
            }

        }
        //set where
        $db_where = [];
        if (!empty($options['where'])) {
            foreach ($options['where'] as $where) {
                foreach ($where as $k => $v) {
                    $fs = array_map(function ($_v) {
                        return $this->alias . '.`' . $_v . '`';
                    }, explode('|', $k));
                    $db_where[] = [join('|', $fs) => $v];
                }
            }
        }
        //set haswhere
        if (!empty($options['hasWhere'])) {
            foreach ($options['hasWhere'] as $with => $hasWhere) {
                if ($that = self::pathFindModel($this, $with)) {
                    foreach ($hasWhere as $k => $v) {
                        $db_where[] = [$that->alias . '.' . $k => $v];
                    }
                }
            }
        }
        $db_options['where'] = $db_where;

        //set order
        if (!empty($options['order'])) {
            $order = [];
            foreach ($options['order'] as $k => $v) {
                if (is_numeric($k)) {
                    $order[] = $this->alias . '.`' . $v . '` ASC';
                } else {
                    $v = strtoupper($v);
                    $order[] = $this->alias . '.`' . $k . '` ' . (in_array($v, ['ASC', 'DESC']) ? $v : 'ASC');
                }
            }
            $db_options['order'] = join(',', $order);
        }
        //set limit
        if (!empty($options['limit'])) {
            $db_options['limit'] = $options['limit'];
        }
        //set field
        $db_options['__result_with'] = $options['with'] ?? [];
        $db_options['__result_alias_with'] = [];
        if (!empty($options['with'])) {
            $fields = [];
            foreach ($options['with'] as $with) {
                //每段都要查找
                $with_explode = explode('.', $with);
                $pre = '';
                foreach ($with_explode as $item) {
                    $fn = null;
                    if (false !== $pos = strpos($item, ':')) {
                        list($item, $fn) = explode(':', $item);
                    }
                    $item_with = ($pre ? $pre . '.' : '') . $item;
                    $pre .= $item;
                    if ($model = self::pathFindModel($this, $item_with)) {
                        $db_options['__result_alias_with'][$model->alias] = $item_with;
                        if ($model->is_has_many) {
                            if (empty($fn)) {
                                $func = 'COUNT';
                                $f = 'id';
                            } else {
                                if (false !== strpos($fn, '|')) {
                                    list($f, $func) = explode('|', $fn);
                                    if (!in_array(strtoupper($func), ['COUNT', 'AVG', 'SUM', 'MAX', 'MIN'])) {
                                        $func = 'COUNT';
                                    }
                                } else {
                                    $f = $fn;
                                    $func = 'COUNT';
                                }
                            }
                            $fields[] = $func . '(' . $model->alias . '.`' . $f . '` ' . ') ' . $model->alias . $func . '_' . $f;

                        } else {
                            $seted_feild = '';
                            foreach ($db_options['join'] as $join) {
                                if ($join['alias'] === $model->alias) {
                                    if ($join['field']) {
                                        $seted_feild = $join['field'];
                                        break;
                                    }
                                }
                            }
                            if (empty($seted_feild)) {
                                if (false === $db_fields = $model->getDbFields()) {
                                    return false;
                                }
                                $fields = array_merge($fields, array_map(function ($field) use ($model) {
                                    return $model->alias . '.`' . $field . '` ' . $model->alias . $field;
                                }, array_column($db_fields, 'field')));
                            } else {
                                $fields = array_merge($fields, array_map(function ($field) use ($model) {
                                    return $model->alias . '.`' . $field . '` ' . $model->alias . $field;
                                }, explode(',', $seted_feild)));
                            }
                        }
                    }
                    unset($model);
                }
            }
            $fields = array_unique($fields);
            array_unshift($fields, $this->alias . '.*');
            $db_options['field'] = join(',', $fields);

        }
        return $db_options;
    }

    private static function completeData(&$result, $with_path)
    {
        if (empty($result)) {
            return;
        }
        //重新根据path组合
        foreach ($with_path as $alias => $position) {
            $position_path = explode('.', $position);
            $cp =& $result;
            foreach ($position_path as $path) {
                $cp =& $cp[$path];
            }
            foreach ($result as $key => $item) {
                $len = strlen($alias);
                if (substr($key, 0, $len) === $alias) {
                    unset($result[$key]);
                    if (is_null($item)) {
                        continue;
                    }
                    if (is_null($cp)) {
                        $cp = [];
                    }
                    $cp[substr($key, $len)] = $item;
                }
            }
            unset($cp);
        }
    }

    protected function after(array &$result, array $with, array $with_value)
    {

    }


    /**
     * @param $with
     * @return Relation|null
     */
    protected function getRelation($with)
    {
        if (!array_key_exists($with, $this->relations)) {
            if (method_exists($this, $with)) {
                $this->relations[$with] = call_user_func([$this, $with]);
            }
        }
        if (empty($this->relations[$with]) || !$this->relations[$with] instanceof Relation) {
            //防止写错
            unset($this->relations[$with]);
            return null;
        }
        if ($this->relations[$with]->getType() === 'hasMany') {
            $this->options['group'] = true;
            $this->relations[$with]->getModel()->is_has_many = true;
        }
        return $this->relations[$with];
    }

    private static function getJoinOptions($that, $withs): array
    {
        $options = [];
        if (!empty($withs)) {
            foreach ($withs as $with => $sub_with) {
                /**@var Relation $relation */
                /**@var Model $that */
                if ($relation = $that->getRelation($with)) {
                    $options = array_merge($options, [[
                        'table' => $relation->getModel()->table,
                        'alias' => $relation->getModel()->alias,
                        'on' => $relation->getOn(),
                        'type' => $relation->getJoinType(),
                        'field' => $relation->getField(),
                    ]], $relation->getType() !== 'hasMany' ? self::getJoinOptions($relation->getModel(), self::parseWith($sub_with)) : []);
                }
            }
        }
        return $options;
    }

    /**
     * 解析一级with
     * @param array $with
     * @return array
     */
    private static function parseWith(array $with = []): array
    {
        $_with = [];
        if (!empty($with)) {
            foreach ($with as $item) {
                if (empty($item)) {
                    continue;
                }
                $exp = explode('.', $item, 2);
                //忽略hasMany的fn
                if (false !== $pos = strpos($exp[0], ':')) {
                    $exp[0] = substr($exp[0], 0, $pos);
                }
                unset($pos);
                $_with[$exp[0]][] = $exp[1] ?? '';
            }
        }
        return $_with;
    }


    /**
     * user.profile.user.news
     * 通过with的path获取model
     *不是正确路径返回null
     * @param Model $that
     * @param string $with
     * @return self|null
     */
    private static function pathFindModel(Model $that, string $with)
    {
        $path = explode('.', $with);
        foreach ($path as $item) {
            if ($relation = $that->getRelation($item)) {
                $that = $relation->getModel();
            } else {
                return null;
            }
        }
        return $that;
    }


    protected function belongsTo(string $class, string $fk = '', string $pk = 'id'): Relation
    {
        return new Relation($this, new $class, $fk, $pk, 'belongsTo');
    }

    protected function hasOne(string $class, string $fk = '', string $pk = 'id'): Relation
    {
        return new Relation($this, new $class, $fk, $pk, 'hasOne');
    }

    protected function hasMany(string $class, string $fk = '', string $pk = 'id'): Relation
    {
        //hasMany 主要用来约束或者取聚合结果
        return new Relation($this, new $class, $fk, $pk, 'hasMany');
    }

    private $_db_fields = [];


    public function getDbFields()
    {
        $config = Config::getInstance()->get('app');
        $debug = $config['debug'] ?? false;
        if (!$debug) {
            $this->_db_fields = Cache::getInstance()->get('db_fields_of_' . $this->table) ?: [];
        }
        if (!empty($this->_db_fields)) {
            return $this->_db_fields;
        }
        $sql = 'SHOW COLUMNS FROM `' . $this->table . '`';
        if (false === $this->_db_fields = Db::getInstance()->query($sql)) {
            $this->error = '获取`' . $this->table . '`结构失败';
            return false;
        }
        Cache::getInstance()
            ->set('db_fields_of_' . $this->table, $this->_db_fields);
        return $this->_db_fields;
    }

    private $error = '';

    /**
     * @return string
     */
    public function getError(): string
    {
        return $this->error;
    }

}